#include "ayoo/mysqlwrap.h"
#include "ayoo/exception.h"
#include <errno.h>

namespace AYOO{

MySQLWrap:: MySQLWrap()
: m_sHost(""), m_iPort(0), m_sUser(""), m_sPswd("")
{
	m_bConnected = false;
	m_iField = 0;
	m_result = NULL;

	m_bFieldIndexInitialized = false;
	m_bIfConfig = false;
}


MySQLWrap:: ~MySQLWrap()
{
	this->Close();
}

int32_t MySQLWrap:: Close()
{
	if (m_bConnected)
	{
		FreeResult();
		mysql_close(&m_connection);
		m_bConnected= false;
	}
	return 0;
}

int32_t MySQLWrap:: EscapeString (std::string &str)
{
	if (str.size()==0) return 0;

	char *buff= new char[str.size()*2+1];
	mysql_escape_string (buff, str.c_str(), str.size());
	str= buff;
	delete[] buff;
	return 0;
}

int32_t MySQLWrap::Connect(const std::string &sHost,  uint32_t iPort,  const std::string &sUser, const std::string &sPswd)
{
	if (!m_bConnected)
	{
		this->Close();
		m_sHost = sHost;
		m_iPort = iPort;
		m_sUser = sUser;
		m_sPswd = sPswd;
		m_bIfConfig = true;
		return this->ConnectReal();
	}
	return 0;
}

int32_t MySQLWrap:: ConnectReal()
{
	if(!m_bIfConfig)
	{
		throw AyooException(-1, "Config for MySQLWrap is not ready. Invoke Connect(host,port,user,pswd) instead.");
	}

	mysql_init (&m_connection);
	if (mysql_real_connect(&m_connection, m_sHost.c_str(), m_sUser.c_str(), m_sPswd.c_str(), NULL, m_iPort, NULL, 0) == NULL)
	{
		throw AyooException(mysql_errno(&m_connection), "connect[-h%s -u%s -p%s port:%d] fail.\nError %u (%s)\n",
				m_sHost.c_str(), m_sUser.c_str(), m_sPswd.c_str(), m_iPort,
				mysql_errno(&m_connection), mysql_error(&m_connection));
	}
	m_bConnected = true;
	return 0;
}

int32_t MySQLWrap:: Query(const std::string &sSQL)
{
	//Connect();
	if ( !(this->m_bConnected) )
	{
		this->ConnectReal();
	}

	if (mysql_real_query( &m_connection, sSQL.c_str(), sSQL.size() ) != 0)
	{
		throw AyooException(mysql_errno(&m_connection), "mysql Query fail [%s].\nError=[%s]\nSQL=%s", 
				m_sHost.c_str(),  
				mysql_error(&m_connection), 
				sSQL.c_str());
	}
	return 0;
}

int32_t MySQLWrap:: QueryWith(const char* szSQLfmt, ...)
{
	uint32_t uiSize = strlen(szSQLfmt);
	va_list args;
	va_start( args, szSQLfmt);
		uiSize += this->GuessParamSize(szSQLfmt, args);
		char szSQL[uiSize];
		memset(szSQL, 0x00, uiSize);
		vsprintf(szSQL, szSQLfmt, args);
		if(0x00!=szSQL[uiSize-1])
			szSQL[uiSize-1] = 0x00;
	va_end( args );

	return this->Query( szSQL );

}


uint32_t MySQLWrap:: GuessParamSize(const char* szSQLfmt, va_list args)
{
	uint32_t uiSize = 0;
	char* p = NULL;
	for ( uint32_t i= 0; szSQLfmt[i]!='\0' ; i++ )
	{
		if ( szSQLfmt[i] == '%' )
		{
			switch(szSQLfmt[i+1])
			{
				case '%': break;
				case 's':
				case 'S':
					p = va_arg(args, char*);
					if( NULL==p )
						throw AyooException(-1, "SQL-Value is not ready, at:%d", i);
					uiSize += strlen( p );
					break;
				#ifndef NOFLOAT
				case 'E':
				case 'G':
				case 'e':
				case 'f':
				case 'g':
					va_arg(args, double);
					uiSize += 32;
					break;
				#endif
				case 'l':
					va_arg(args, long);
					uiSize += 32;
					break;
				default:
					va_arg(args, void*);
					uiSize += 32;
					break;
			}
			i++;
		}
	}
	return uiSize;

}

int32_t MySQLWrap:: FreeResult()
{
	if (m_result != NULL)
		mysql_free_result (m_result);
	m_iField = 0;
	m_result = NULL;
	if (m_bFieldIndexInitialized)
	{
		m_FieldIndex.erase(m_FieldIndex.begin(), m_FieldIndex.end());
		m_bFieldIndexInitialized = false;
	}
	return 0;
}

int32_t MySQLWrap:: StoreResult()
{
	FreeResult();
	m_result = mysql_store_result (&m_connection);
	if (m_result == NULL)
	{
		throw AyooException(mysql_errno(&m_connection), "fail to store result:%s", mysql_error(&m_connection));
	}
	m_iField = mysql_num_fields (m_result);
	m_iRows = mysql_num_rows (m_result);
	return 0;
}

char** MySQLWrap:: FetchRow()
{
	if (m_result == NULL)
		StoreResult();
	m_row = mysql_fetch_row (m_result);
	return m_row;
}

int32_t MySQLWrap:: InitFieldName()
{
	if ((!m_bFieldIndexInitialized) && (m_result!=NULL))
	{
		uint32_t i;
		MYSQL_FIELD *fields;

		fields = mysql_fetch_fields(m_result);
		for(i = 0; i < m_iField; i++)
		{
			m_FieldIndex[fields[i].name] = i;
		}
		m_bFieldIndexInitialized = true;
	}
	return 0;
}

const char* MySQLWrap:: GetFieldName(int32_t iField)
{
	if (m_result==NULL) {
		return NULL;
	}
	MYSQL_FIELD *fields;
	fields = mysql_fetch_fields(m_result);
	if ((uint32_t)iField> m_iField) {
		return NULL;
	}
	return fields[iField].name;
}

int32_t MySQLWrap:: GetAffectedRows()
{
	my_ulonglong iNumRows;

	if (!m_bConnected) return 0;
	iNumRows = mysql_affected_rows(&m_connection);

	return (int)iNumRows;
}

uint32_t MySQLWrap:: GetLastInsertId ()
{
	return (uint32_t)mysql_insert_id(&m_connection);
}


char* MySQLWrap:: GetField(char* szFieldName)
{
	InitFieldName();
	return GetField(m_FieldIndex[szFieldName]);
}

int MySQLWrap:: GetField_Int(char* szFieldName)
{
	char* szValue = this->GetField(szFieldName);
	return (NULL==szValue)?0:atoi(szValue);
}

float MySQLWrap:: GetField_Float(char* szFieldName)
{
	char* szValue = this->GetField(szFieldName);
	return (NULL==szValue)?0:atof(szValue);
}

const char* MySQLWrap:: GetField_String(char* szFieldName)
{
	char* szValue = this->GetField(szFieldName);
	return (NULL==szValue)?"":szValue;
}




char* MySQLWrap:: GetField(uint32_t iField)
{
	if (iField > m_iField)
		return NULL;
	return m_row[iField];
}


uint32_t MySQLWrap::GetLastError(std::string & sErrMsg)
{
	sErrMsg = mysql_error(&m_connection);
	return mysql_errno(&m_connection);
}

}; /*namespace:AYOO*/

